#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

enum Status
{
	NEW,
	CONNECTING,
	CONNECTED,
	DISCONNECT,
	CLOSED
};

enum ExitCode
{
	SUCCESS,
	USAGE_ERR,
	SOCKET_ERR,
	CONNECT_ERR
};

const int default_sockfd = -1;
const int default_retrytimes = 1;
const int default_maxretry = 7;

class TcpConnect
{
public:
	TcpConnect(std::string ip, uint16_t port)
		: _sockfd(default_sockfd),
		  _ip(ip),
		  _port(port),
		  _retrytimes(default_retrytimes),
		  _maxretry(default_maxretry),
		  _status(NEW)
	{
	}

	void Connect()
	{
		_status = CONNECTING;
		_sockfd = socket(AF_INET, SOCK_STREAM, 0);
		if (_sockfd < 0)
		{
			std::cerr << "socket error: " << std::endl;
			exit(SOCKET_ERR);
		}

		// 设置协议族
		struct sockaddr_in addr;
		memset(&addr, 0, sizeof(addr));
		addr.sin_family = AF_INET;
		addr.sin_port = htons(_port);
		inet_pton(AF_INET, _ip.c_str(), &addr.sin_addr);

		// 连接服务器
		int n = connect(_sockfd, (struct sockaddr *)&addr, sizeof(addr));
		if (n < 0)
		{
			std::cerr << "connect error" << std::endl;
			_status = DISCONNECT;
			return;
		}

		_status = CONNECTED;
	}

	void ReConnect()
	{
		_status = CONNECTING;
		int trytimes = 0;
		while (trytimes < _maxretry)
		{
			Connect();
			if (_status == CONNECTED)
			{
				return;
			}
			trytimes++;
			std::cout << "第" << trytimes << "次重连失败" << std::endl;
			sleep(_retrytimes);
		}

		std::cout<<"已超出重连次数，请检查网络或服务器状态"<<std::endl;
		_status = CLOSED;
	}

	void Process()
	{
		while (true)
		{
			std::cout << "Please Enter# ";
			std::string input;
			std::getline(std::cin, input);

			int n = send(_sockfd, input.c_str(), input.size(), 0);
			if (n < 0)
			{
				std::cerr << "send error: " << std::endl;
				ReConnect();
				return;
			}
			else if (n == 0)
			{
				_status = CLOSED;
				std::cout << "server close" << std::endl;
				break;
			}
			else
			{
				char buffer[4096];
				int m = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
				if (m > 0)
				{
					buffer[m] = 0;
					std::cout << "Server# " << buffer << std::endl;
				}
				else{
					_status = CLOSED;
					std::cout << "server close" << std::endl;
				}
			}
		}
	}
	void DisConnect() {
		if(_sockfd != default_sockfd){
			close(_sockfd);
			_sockfd = default_sockfd;
			_status = CLOSED;
		}
	}
	~TcpConnect() {
		DisConnect();
	}

	int GetStatus() const
	{
		return _status;
	}

private:
	int _sockfd;
	std::string _ip;
	uint16_t _port;
	int _retrytimes; // 断线重连时间间隔
	int _maxretry;	 // 最大重连次数
	Status _status;
};

class TcpClient
{
public:
	TcpClient(std::string ip, uint16_t port) : _tcp(ip, port)
	{
	}

	void Excute()
	{
		while (true)
		{
			int status = _tcp.GetStatus();
			switch (status)
			{
			case NEW:
				std::cout << "首次申请连接，正在建立新连接" << std::endl;
				_tcp.Connect();
				break;
			case CONNECTED:
				std::cout << "连接成功，正在进行通信" << std::endl;
				_tcp.Process();
				break;
			case DISCONNECT:
				std::cout << "连接断开，正在重新连接" << std::endl;
				_tcp.ReConnect();
				break;
			case CLOSED:
				std::cout << "连接已关闭" << std::endl;
				_tcp.DisConnect();
				return;
			default:
				break;
			}
			sleep(1);
		}
	}

	~TcpClient() {}

private:
	TcpConnect _tcp;
};

// ./tcpclient server_ip server_port
int main(int argc, char *argv[])
{
	if (argc != 3)
	{
		std::cerr << "Usage: ./tcpclient server_ip server_port" << std::endl;
		exit(USAGE_ERR);
	}

	std::string server_ip = argv[1];
	uint16_t server_port = std::stoi(argv[2]);

	TcpClient tcpclient(server_ip, server_port);
	tcpclient.Excute();

	return 0;
}